package files;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.HeadlessException;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.LinkedList;
import javax.imageio.ImageIO;
import javax.swing.JOptionPane;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.DOMException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import snazzysprites.SnazzySprites;
import static snazzysprites.SnazzySpritesSettings.*;
import gui.PoseCanvas;
import gui.SnazzySpritesGUI;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import shapes.SnazzyEllipse;
import shapes.SnazzyLine;
import shapes.SnazzyRectangle;
import shapes.SnazzyShape;
import shapes.SnazzyShapeType;
import state.Animation;
import state.Pose;
import state.SnazzySpritesState;
import state.SnazzySpritesStateManager;
import state.SpriteType;

/**
 * This class performs Sprite object saving to an XML formatted .snazzy file, loading
 * such files, and exporting Animation objects to image files.
 * 
 * @author  Richard McKenna
 *          Debugging Enterprises
 * @author Andrew Gratta
 * @version 1.0
 */
public class SpriteTypeIO 
{
    /**
     * This method loads the contents of the spriteFileName .snazzy file into
     * the Sprite for editing. Note that this file must validate against
     * the snazzy_sprite.xsd schema.
     * 
     * @param spriteTypeFileName The Sprite file to load for editing.
     */
    public void loadSpriteType(String spriteTypeFileName)
    {
        // THIS WILL HELP US LOAD STUFF
        XMLUtilities xmlUtil = new XMLUtilities();
        
        // WE'RE GOING TO LOAD IT INTO THE POSE
        
        try
        {
            // LOAD THE XML FILE
            Document doc = xmlUtil.loadXMLDocument(spriteTypeFileName, SPRITE_SCHEMA);
            
            // AND THEN EXTRACT ALL THE DATA
            
            int numAnimations = xmlUtil.getIntData(doc, NUM_ANIMATIONS_NODE);
            SpriteType tempSpriteType = new SpriteType();
            Node spriteTypeNode = xmlUtil.getChildNodeWithName(doc, SNAZZY_SPRITE_NODE);
            Node animationListNode = xmlUtil.getChildNodeWithName(spriteTypeNode, ANIMATION_LIST_NODE);
            Node animationNode = xmlUtil.getChildNodeWithName(animationListNode, ANIMATION_NODE);            
            while(animationNode != null)
            {
                if (!animationNode.hasChildNodes())
                {
                    animationNode = animationNode.getNextSibling();
                    continue;
                }
                Node animationNameNode = xmlUtil.getChildNodeWithName(animationNode, ANIMATION_NAME_NODE);
                String animationName = animationNameNode.getTextContent();
                int animationWidth = Integer.parseInt(xmlUtil.getChildNodeWithName(
                        animationNode, ANIMATION_WIDTH_NODE).getTextContent());
                int animationHeight = Integer.parseInt(xmlUtil.getChildNodeWithName(
                        animationNode, ANIMATION_HEIGHT_NODE).getTextContent());
                Animation animation = new Animation(animationName, animationWidth, animationHeight, false);            
                tempSpriteType.addAnimation(animation);
                Node poseListNode = xmlUtil.getChildNodeWithName(animationNode, POSE_LIST_NODE);
                Node poseNode = xmlUtil.getChildNodeWithName(poseListNode, POSE_NODE);
                while (poseNode != null)
                {
                    if (!poseNode.hasChildNodes())
                    {
                        poseNode = poseNode.getNextSibling();
                        continue;
                    }
                    Pose pose = new Pose(animationWidth, animationHeight);
                    Node durationNode = xmlUtil.getChildNodeWithName(poseNode, POSE_DURATION_NODE);
                    pose.setPoseDuration(Integer.parseInt(durationNode.getTextContent()));
                    LinkedList<SnazzyShape> shapesList = pose.getShapesList();
            
                    // LET'S GET THE SHAPE LIST
                    Node shapeListNode = xmlUtil.getChildNodeWithName(poseNode, SHAPES_LIST_NODE);
                    Node shapeNode = xmlUtil.getChildNodeWithName(shapeListNode, SNAZZY_SHAPE_NODE);
                    while(shapeNode != null)
                    {
                        if (!shapeNode.hasChildNodes())
                        {
                            shapeNode = shapeNode.getNextSibling();
                            continue;
                        }
                        // GET THE NODE, THEN WE'LL EXTRACT DATA FROM IT
                        // TO FILL IN THE shapeToAdd SHAPE
                        SnazzyShape shapeToAdd;

                        // WHAT TYPE IS IT?
                        Node geometryNode = xmlUtil.getChildNodeWithName(shapeNode, GEOMETRY_NODE);
                        NamedNodeMap attributes = geometryNode.getAttributes();
                        String shapeTypeText = attributes.getNamedItem(SHAPE_TYPE_ATTRIBUTE).getTextContent();
                        SnazzyShapeType shapeType = SnazzyShapeType.valueOf(shapeTypeText);

                        if (shapeType == SnazzyShapeType.RECTANGLE)
                        {
                            double x = Double.parseDouble(attributes.getNamedItem(X_ATTRIBUTE).getTextContent());
                            double y = Double.parseDouble(attributes.getNamedItem(Y_ATTRIBUTE).getTextContent());
                            double width = Double.parseDouble(attributes.getNamedItem(WIDTH_ATTRIBUTE).getTextContent());
                            double height = Double.parseDouble(attributes.getNamedItem(HEIGHT_ATTRIBUTE).getTextContent());
                            Rectangle2D.Double geometry = new Rectangle2D.Double(x, y, width, height);
                            shapeToAdd = new SnazzyRectangle(geometry);
                        }
                        else if (shapeType == SnazzyShapeType.ELLIPSE)
                        {
                            double x = Double.parseDouble(attributes.getNamedItem(X_ATTRIBUTE).getTextContent());
                            double y = Double.parseDouble(attributes.getNamedItem(Y_ATTRIBUTE).getTextContent());
                            double width = Double.parseDouble(attributes.getNamedItem(WIDTH_ATTRIBUTE).getTextContent());
                            double height = Double.parseDouble(attributes.getNamedItem(HEIGHT_ATTRIBUTE).getTextContent());
                            Ellipse2D.Double geometry = new Ellipse2D.Double(x, y, width, height);
                            shapeToAdd = new SnazzyEllipse(geometry);
                        }
                        else if (shapeType == SnazzyShapeType.LINE)
                        {
                            double x1 = Double.parseDouble(attributes.getNamedItem(X1_ATTRIBUTE).getTextContent());
                            double y1 = Double.parseDouble(attributes.getNamedItem(Y1_ATTRIBUTE).getTextContent());
                            double x2 = Double.parseDouble(attributes.getNamedItem(X2_ATTRIBUTE).getTextContent());
                            double y2 = Double.parseDouble(attributes.getNamedItem(Y2_ATTRIBUTE).getTextContent());
                            Line2D.Double geometry = new Line2D.Double(x1,y1,x2,y2);
                            shapeToAdd = new SnazzyLine(geometry);
                        }
                        else continue; 

                        // FIRST GET THE OUTLINE THICKNESS
                        Node outlineNode = xmlUtil.getChildNodeWithName(shapeNode, OUTLINE_THICKNESS_NODE);
                        int outlineThickness = Integer.parseInt(outlineNode.getTextContent());
                        BasicStroke outlineStroke = new BasicStroke(outlineThickness);

                        // THEN THE OUTLINE COLOR
                        Color outlineColor = extractColor(xmlUtil, shapeNode, OUTLINE_COLOR_NODE);

                        // THEN THE FILL COLOR
                        Color fillColor = extractColor(xmlUtil, shapeNode, FILL_COLOR_NODE);

                        // AND THE TRANSPARENCY
                        Node alphaNode = xmlUtil.getChildNodeWithName(shapeNode, ALPHA_NODE);
                        int alpha = Integer.parseInt(alphaNode.getTextContent());

                        // AND FILL IN THE REST OF THE SHAPE DATA
                        shapeToAdd.setAlpha(alpha);
                        shapeToAdd.setFillColor(fillColor);
                        shapeToAdd.setOutlineColor(outlineColor);
                        shapeToAdd.setOutlineThickness(outlineStroke);

                        // WE'VE LOADED THE SHAPE, NOW GIVE IT TO THE POSE
                        shapesList.add(shapeToAdd);
                        
                        shapeNode = shapeNode.getNextSibling();
                        int x = 0;
                    }
                    animation.addPose(pose);
                    poseNode = poseNode.getNextSibling();
                }
                if (animation.getPoseCount() == 0)
                {
                    animation.addPose(new Pose(animation.getWidth(), animation.getHeight()));
                }
                animationNode = animationNode.getNextSibling();
            }
                        
            
           
            // EVERYTHING HAS LOADED WITHOUT FAILING, SO LET'S
            // FIRST LOAD THE DATA INTO THE REAL POSE
            SnazzySprites singleton = SnazzySprites.getSnazzySprites();
            SnazzySpritesStateManager stateManager = singleton.getStateManager();
            stateManager.setSpriteType(tempSpriteType);
            
            // TELL THE USER ABOUT OUR SUCCESS
            SnazzySpritesGUI gui = singleton.getGUI();
            JOptionPane.showMessageDialog(
                gui,
                SPRITE_LOADED_TEXT,
                SPRITE_LOADED_TITLE_TEXT,
                JOptionPane.INFORMATION_MESSAGE);

            // AND ASK THE GUI TO UPDATE
            singleton.getStateManager().setState(SnazzySpritesState.SELECT_ANIMATION_STATE);
        }
        catch(InvalidXMLFileFormatException | DOMException | HeadlessException ex)
        {
            // SOMETHING WENT WRONG LOADING THE .pose XML FILE
            SnazzySprites singleton = SnazzySprites.getSnazzySprites();
            SnazzySpritesGUI gui = singleton.getGUI();
            JOptionPane.showMessageDialog(
                gui,
                SPRITE_LOADING_ERROR_TEXT,
                SPRITE_LOADING_ERROR_TITLE_TEXT,
                JOptionPane.ERROR_MESSAGE);            
        }    
    }
    
    /**
     * This helper method will extract color information from the first found
     * child node of parentNode with color data and use this to build and
     * return a Color object.
     * 
     * @param xmlUtil This helps perform some XML extraction.
     * 
     * @param parentNode This node has a color child of some sort that
     * will have the color data.
     * 
     * @param colorChildNodeName This is the name of the child node that
     * has the color data.
     * 
     * @return A Color with the color data found in the color node.
     */
    private Color extractColor(XMLUtilities xmlUtil, Node parentNode, String colorChildNodeName)
    {
        // GET THE NODES WE'LL NEED 
        Node colorNode = xmlUtil.getChildNodeWithName(parentNode, colorChildNodeName);
        Node redNode = xmlUtil.getChildNodeWithName(colorNode, RED_NODE);
        Node greenNode = xmlUtil.getChildNodeWithName(colorNode, GREEN_NODE);
        Node blueNode = xmlUtil.getChildNodeWithName(colorNode, BLUE_NODE);

        // CONVERT THE COLOR DATA TO INTEGERS
        int red = Integer.parseInt(redNode.getTextContent());
        int green = Integer.parseInt(greenNode.getTextContent());
        int blue = Integer.parseInt(blueNode.getTextContent());

        // BUILD AND RETURN THE COLOR OBJECT
        Color extractedColor = new Color(red, green, blue);
        return extractedColor;
    }

    /**
     * This method saves the pose currently being edited to the poseFile. Note
     * that it will be saved as a .pose file, which is an XML-format that will
     * conform to the poseur_pose.xsd schema.
     * 
     * @param poseFile The file to write the pose to.
     * 
     * @return true if the file is successfully saved, false otherwise. It's
     * possible that another program could lock out ours from writing to it,
     * so we need to let the caller know when this happens.
     */
    public boolean saveSpriteType(File poseFile)
    {
        // GET THE POSE AND ITS DATA THAT WE HAVE TO SAVE
        SnazzySprites singleton = SnazzySprites.getSnazzySprites();
        SnazzySpritesStateManager stateManager = singleton.getStateManager();
        SpriteType spriteTypeToSave = stateManager.getSpriteType();
        Collection<Animation> animationList = spriteTypeToSave.getAnimationList();
        
        try 
        {
            // THESE WILL US BUILD A DOC
            DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
            DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
            
            // FIRST MAKE THE DOCUMENT
            Document doc = docBuilder.newDocument();
            
            // THEN THE ROOT ELEMENT
            Element rootElement = doc.createElement(SNAZZY_SPRITE_NODE);
            doc.appendChild(rootElement);
 
            // THEN MAKE AND ADD THE WIDTH, HEIGHT, AND NUM SHAPES
//            Element spriteNameElement = makeElement(doc, rootElement, 
//                    SPRITE_NAME_NODE, spriteTypeToSave.getName());
            Element numAnimationsElement = makeElement(doc, rootElement, 
                    NUM_ANIMATIONS_NODE, "" + spriteTypeToSave.getAnimationCount());
            Element animationListElement = makeElement(doc, rootElement, 
                    ANIMATION_LIST_NODE, "");
            for (Animation anim : animationList)
            {
                Element animationNodeElement = makeElement(doc, animationListElement, 
                    ANIMATION_NODE, "");
                Element animationNameElement = makeElement(doc, animationNodeElement, 
                    ANIMATION_NAME_NODE, anim.getName());
                Element animationWidthElement = makeElement(doc, animationNodeElement, 
                    ANIMATION_WIDTH_NODE, "" + anim.getWidth());
                Element animationHeightElement = makeElement(doc, animationNodeElement, 
                    ANIMATION_HEIGHT_NODE, "" + anim.getHeight());
                Element numPosesElement = makeElement(doc, animationNodeElement,
                    NUM_POSES_NODE, "" + anim.getPoseCount());
                
                Iterator<Pose> poseIter = anim.getPoseIterator();
                
                Element poseListElement = makeElement(doc, animationNodeElement, 
                    POSE_LIST_NODE, "");
                
                while (poseIter.hasNext())
                {
                    Pose pose = poseIter.next();
                    Element poseNodeElement = makeElement(doc, poseListElement, 
                        POSE_NODE, "");
                    Element poseDurationElement = makeElement(doc, poseNodeElement, 
                        POSE_DURATION_NODE, "" + pose.getDuration());
                    
                    LinkedList<SnazzyShape> shapesList = pose.getShapesList();
                    
                    Element numShapesElement = makeElement(doc, poseNodeElement,
                            NUM_SHAPES_NODE, "" + shapesList.size());

                    // NOW LET'S MAKE THE SHAPES LIST AND ADD THAT TO THE ROOT AS WELL
                    Element shapesListElement = makeElement(doc, poseNodeElement,
                            SHAPES_LIST_NODE, "");

                    // AND LET'S ADD ALL THE SHAPES TO THE SHAPES LIST
                    for (SnazzyShape shape : shapesList)
                    {
                        // MAKE THE SHAPE NODE AND ADD IT TO THE LIST
                        Element shapeNodeElement = makeElement(doc, shapesListElement,
                                SNAZZY_SHAPE_NODE, "");

                        // NOW LET'S FILL IN THE SHAPE'S DATA

                        // FIRST THE OUTLINE THICKNESS
                        Element outlineThicknessNode = makeElement(doc, shapeNodeElement,
                                OUTLINE_THICKNESS_NODE, "" + (int)(shape.getOutlineThickness().getLineWidth()));

                        // THEN THE OUTLINE COLOR
                        Element outlineColorNode = makeColorNode(doc, shapeNodeElement,
                                OUTLINE_COLOR_NODE, shape.getOutlineColor());

                        // AND THE FILL COLOR
                        Element fillColorNode = makeColorNode(doc, shapeNodeElement,
                                FILL_COLOR_NODE, shape.getFillColor());

                        // AND THE THE ALPHA VALUE
                        Element alphaNode = makeElement(doc, shapeNodeElement,
                                ALPHA_NODE, "" + shape.getAlpha());

                        // SHAPES HAVE BEEN GIVEN THE GIFT OF KNOWING HOW TO
                        // ADD THEMESELVES AND THEIR DATA TO THE SHAPE ELEMENT
                        Element geometryNode = doc.createElement(GEOMETRY_NODE);
                        shape.addNodeData(geometryNode);         
                        shapeNodeElement.appendChild(geometryNode);        
                    }
                }
            }
            
            
            
            // THE TRANSFORMER KNOWS HOW TO WRITE A DOC TO
            // An XML FORMATTED FILE, SO LET'S MAKE ONE
            TransformerFactory transformerFactory = TransformerFactory.newInstance();
            Transformer transformer = transformerFactory.newTransformer();
            transformer.setOutputProperty(OutputKeys.INDENT, YES_VALUE);
            transformer.setOutputProperty(XML_INDENT_PROPERTY, XML_INDENT_VALUE);
            DOMSource source = new DOMSource(doc);
            StreamResult result = new StreamResult(poseFile);
            
            // SAVE THE POSE TO AN XML FILE
            transformer.transform(source, result);    
            
            // WE MADE IT THIS FAR WITH NO ERRORS
            SnazzySpritesGUI gui = singleton.getGUI();
            JOptionPane.showMessageDialog(
                gui,
                SPRITE_SAVED_TEXT,
                SPRITE_SAVED_TITLE_TEXT,
                JOptionPane.INFORMATION_MESSAGE);
            return true;
        }
        catch(TransformerException | ParserConfigurationException | DOMException | HeadlessException ex)
        {
            // SOMETHING WENT WRONG WRITING THE XML FILE
            SnazzySpritesGUI gui = singleton.getGUI();
            JOptionPane.showMessageDialog(
                gui,
                SPRITE_SAVING_ERROR_TEXT,
                SPRITE_SAVING_ERROR_TITLE_TEXT,
                JOptionPane.ERROR_MESSAGE);  
            return false;
        }    
    }
    
    /**
     * This helper method can be used to build a node to store color data. We'll
     * use this for building a Doc for the purpose of saving it to a file.
     * 
     * @param doc The document where we'll put the node.
     * 
     * @param parent The node where we'll add the color node as a child.
     * 
     * @param elementName The name of the color node to make. Colors may be
     * used for different purposes, so may have different names.
     * 
     * @param color The color data we'll use to build the node is in here.
     * 
     * @return A Element (i.e. Node) with the color information inside.
     */
    private Element makeColorNode(Document doc, Element parent, String elementName, Color color)
    {
        // MAKE THE COLOR NODE
        Element colorNode = makeElement(doc, parent, elementName, "");
        
        // AND THE COLOR COMPONENTS
        Element redNode = makeElement(doc, colorNode, RED_NODE, "" + color.getRed());
        Element greenNode = makeElement(doc, colorNode, GREEN_NODE, "" + color.getGreen());
        Element blueNode = makeElement(doc, colorNode, BLUE_NODE, "" + color.getBlue());
        
        // AND RETURN OUR NEW ELEMENT (NODE)
        return colorNode;
    }

    /**
     * This helper method builds elements (nodes) for us to help with building
     * a Doc which we would then save to a file.
     * 
     * @param doc The document we're building.
     * 
     * @param parent The node we'll add our new node to.
     * 
     * @param elementName The name of the node we're making.
     * 
     * @param textContent The data associated with the node we're making.
     * 
     * @return A node of name elementName, with textComponent as data, in the doc
     * document, with parent as its parent node.
     */
    private Element makeElement(Document doc, Element parent, String elementName, String textContent)
    {
        Element element = doc.createElement(elementName);
        element.setTextContent(textContent);
        parent.appendChild(element);
        return element;
    }
    
    /**
     * Exports the current sprite type to an image file
     * of the same name in the ./data/exported_images 
     * directory.
     * 
     * @param currentSpriteTypeName Name of the pose to export.
     */
    public void exportSpriteType(String currentSpriteTypeName)
    {   
        // WE DON'T HAVE TO ASK THE USER, WE'LL JUST EXPORT IT
        // FIRST GET THE STUFF WE'LL NEED
        SnazzySprites singleton = SnazzySprites.getSnazzySprites();
        SnazzySpritesGUI gui = singleton.getGUI();
        SnazzySpritesStateManager state = singleton.getStateManager();
        PoseCanvas canvas = gui.getZoomablePoseCanvas();
        
        String spriteTypeFolderName = EXPORTED_IMAGES_PATH + currentSpriteTypeName;
        File spriteTypeFolder = new File(spriteTypeFolderName);
        spriteTypeFolder.mkdir();
        
        File spriteTypeXMLFile = new File(spriteTypeFolderName + "/" + currentSpriteTypeName + ".xml");
        exportToXML(spriteTypeXMLFile);
        
        for (Animation anim : state.getSpriteType().getAnimationList())
        {
            String animName = anim.getName();
            int poseIndex = 0;
            Iterator<Pose> poseIterator = anim.getPoseIterator();
            while (poseIterator.hasNext())
            {
                Pose pose = poseIterator.next();
                // THEN MAKE OUR IMAGE THE SAME DIMENSIONS AS THE POSE
                BufferedImage imageToExport = new BufferedImage(pose.getPoseWidth(), 
                                                                pose.getPoseHeight(),
                                                                BufferedImage.TYPE_INT_ARGB);

                canvas.paintToImage(imageToExport, pose);

                // AND NOW SAVE THE IMAGE TO A .png FILE
                File imageFile = new File(spriteTypeFolderName + "/"
                                            + animName + poseIndex++ 
                                            + PNG_FILE_EXTENSION);
                // LET'S SAVE THE FILE
                try
                {
                    ImageIO.write(imageToExport, PNG_FORMAT_NAME, imageFile);

                }
                catch(IOException ioe)
                {
                    JOptionPane.showMessageDialog(
                        gui,
                        IMAGE_EXPORTING_ERROR_TEXT,
                        IMAGE_EXPORTING_ERROR_TITLE_TEXT,
                        JOptionPane.ERROR_MESSAGE);            
                }
            }
        }

        JOptionPane.showMessageDialog(
        gui,
        IMAGE_EXPORTED_TEXT + currentSpriteTypeName + SPRITE_FILE_EXTENSION,
        IMAGE_EXPORTED_TITLE_TEXT,
        JOptionPane.INFORMATION_MESSAGE);
    }
    
    private void exportToXML(File spriteTypeFile)
    {
        SnazzySprites singleton = SnazzySprites.getSnazzySprites();
        SnazzySpritesStateManager stateManager = singleton.getStateManager();
        SpriteType spriteTypeToSave = stateManager.getSpriteType();
        Collection<Animation> animationList = spriteTypeToSave.getAnimationList();
        
        try 
        {
            // THESE WILL US BUILD A DOC
            DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
            DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
            
            // FIRST MAKE THE DOCUMENT
            Document doc = docBuilder.newDocument();
            
            // THEN THE ROOT ELEMENT
            Element rootElement = doc.createElement(EXPORTED_SPRITE_TYPE_NODE);
            
            doc.appendChild(rootElement);
 
            Element spriteTypeWidthNode = makeElement(doc, rootElement,
                    EXPORTED_SPRITE_TYPE_WIDTH_NODE,
                    stateManager.getSpriteType().getMaxAnimationWidth() + "");
            Element spriteTypeHeightNode = makeElement(doc, rootElement,
                    EXPORTED_SPRITE_TYPE_HEIGHT_NODE,
                    stateManager.getSpriteType().getMaxAnimationHeight() + "");            
            Element imagesListNode = makeElement(doc, rootElement, 
                    EXPORTED_SPRITE_TYPE_IMAGE_LIST_NODE,"");
            
            Element animationListElement = makeElement(doc, rootElement, 
                    EXPORTED_SPRITE_TYPE_ANIMATION_LIST_NODE, "");
            
            int imageId = 1;
            
            for (Animation anim : animationList)
            {
                Element animationStateNode = makeElement(doc, animationListElement, 
                    EXPORTED_SPRITE_TYPE_ANIMATION_STATE_NODE, "");
                Element stateNode = makeElement(doc, animationStateNode, 
                    EXPORTED_SPRITE_TYPE_STATE_NODE, anim.getName());
                Element animationSequenceNode = makeElement(doc, animationStateNode, 
                    EXPORTED_SPRITE_TYPE_ANIMATION_SEQUENCE_NODE, "");
                
                Iterator<Pose> poseIter = anim.getPoseIterator();
                int poseNumber = 1;
                while (poseIter.hasNext())
                {
                    Pose pose = poseIter.next();
                    
                    Element imageNode = makeElement(doc, imagesListNode,
                        EXPORTED_SPRITE_TYPE_IMAGE_FILE_NODE, "");
                    imageNode.setAttribute(EXPORTED_SPRITE_TYPE_IMAGE_FILE_ID_NODE, imageId+"");
                    imageNode.setAttribute(EXPORTED_SPRITE_TYPE_IMAGE_FILE_NAME_NODE, 
                            anim.getName() + (poseNumber - 1) + PNG_FILE_EXTENSION);
                    ++poseNumber;
                    Element poseNode = makeElement(doc, animationSequenceNode, 
                        EXPORTED_SPRITE_TYPE_POSE_NODE, "");
                    poseNode.setAttribute(EXPORTED_SPRITE_TYPE_POSE_IMAGE_ID_NODE, "" + imageId++);
                    poseNode.setAttribute(EXPORTED_SPRITE_TYPE_POSE_DURATION_NODE, "" + pose.getDuration());
                    
                }
            }
            
            
            
            // THE TRANSFORMER KNOWS HOW TO WRITE A DOC TO
            // An XML FORMATTED FILE, SO LET'S MAKE ONE
            TransformerFactory transformerFactory = TransformerFactory.newInstance();
            Transformer transformer = transformerFactory.newTransformer();
            transformer.setOutputProperty(OutputKeys.INDENT, YES_VALUE);
            transformer.setOutputProperty(XML_INDENT_PROPERTY, XML_INDENT_VALUE);
            DOMSource source = new DOMSource(doc);
            StreamResult result = new StreamResult(spriteTypeFile);
            
            // SAVE THE POSE TO AN XML FILE
            transformer.transform(source, result);    
            
            // WE MADE IT THIS FAR WITH NO ERRORS
            SnazzySpritesGUI gui = singleton.getGUI();
            JOptionPane.showMessageDialog(
                gui,
                SPRITE_SAVED_TEXT,
                SPRITE_SAVED_TITLE_TEXT,
                JOptionPane.INFORMATION_MESSAGE);
        }
        catch(TransformerException | ParserConfigurationException | DOMException | HeadlessException ex)
        {
            // SOMETHING WENT WRONG WRITING THE XML FILE
            SnazzySpritesGUI gui = singleton.getGUI();
            JOptionPane.showMessageDialog(
                gui,
                SPRITE_SAVING_ERROR_TEXT,
                SPRITE_SAVING_ERROR_TITLE_TEXT,
                JOptionPane.ERROR_MESSAGE);  
        }
    }
}